using System;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using System.Globalization;
using Microsoft.Practices.CompositeWeb.Utility;
using System.Web;
using Microsoft.Practices.CompositeWeb.Interfaces;
using Microsoft.Practices.CompositeWeb;
using DataAccess;
using DataAccess.BusinessEntities;
using Microsoft.Practices.ObjectBuilder;

namespace Locator.Products
{
    public class ProductsController : IProductsController
    {
        private bool disposed;
        private readonly TraceManager traceManager;
        private readonly LogWriter logWriter;
        private readonly IUnitOfWork unitOfWork;

        public ProductsController([CreateNew] IUnitOfWork unitOfWork)
        {
            this.unitOfWork = unitOfWork;
            this.logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
            this.traceManager = new TraceManager(this.logWriter);
        }

        public List<UserProduct> GetProductsForCurrentUser()
        {
            List<UserProduct> result = new List<UserProduct>();
            using (traceManager.StartTrace("General"))
            {
                var user = this.GetUserEntity();
                string name = user.Username;
                //user = this.unitOfWork.Users.FindWhere(u => u.Username == name).Include("Products").Single();
                var userProductIds = from p in user.Products select p.Id;
                var userProducts = from p in this.unitOfWork.Products.FindAll().Include("Container").Include("Substance") where userProductIds.Contains(p.Id) select p;
                foreach (var product in userProducts)
                {
                    result.Add(new UserProduct()
                    {
                        Name = product.Name,
                        UsedByUser = true,
                        Id = product.Id
                    });
                }
            }

            return result;
        }

        public List<UserProduct> FindProductsContainingName(string name)
        {
            List<UserProduct> result2 = new List<UserProduct>();
            using (traceManager.StartTrace("General"))
            {
                var user = this.GetUserEntity();
                var usersProductIds = from p in user.Products select p.Id;
                var usersProducts = this.unitOfWork.Products.FindWhere(p => p.Substance.Name.ToUpper().Contains(name.ToUpper())).Include("Container").Include("Substance");

                foreach (var product in usersProducts)
                {
                    result2.Add(new UserProduct()
                        {
                            Name = product.Name,
                            Id = product.Id,
                            UsedByUser = usersProductIds.Contains(product.Id)
                        });
                }
            }

            return result2;
        }

        public List<Product> FindProductsContainingSubstance(int substanceId)
        {
            List<Product> result = new List<Product>();
            User user = this.GetUserEntity();
            IQueryable<Product> matchingProducts = this.unitOfWork.Products.FindWhere(p => p.Substance.Id == substanceId);
            IEnumerable<int> usersProductIds = from p in user.Products select p.Id;
            foreach (var product in matchingProducts)
            {
                product.UsedByUser = usersProductIds.Contains(product.Id);
                result.Add(product);
            }

            return result;
        }

        public List<Product> FindProductsContainingSubstanceName(string substanceName)
        {
            List<Product> result = new List<Product>();
            var user = this.GetUserEntity();
            var matchingProducts = this.unitOfWork.Products.FindWhere(p => p.Substance.Name == substanceName);
            var usersProductIds = from p in user.Products select p.Id;
            foreach (var product in matchingProducts)
            {
                product.UsedByUser = usersProductIds.Contains(product.Id);
                result.Add(product);
            }

            return result;
        }

        public List<Substance> GetAllSubstances()
        {
            List<Substance> result = new List<Substance>();
            result.AddRange(this.unitOfWork.Substances.FindAll());

            return result;
        }

        public void AddProductForCurrentUser(int productId)
        {
            User user = this.GetUserEntity();
            var product = (this.unitOfWork.Products.FindWhere(p => p.Id == productId)).FirstOrDefault();
            if (product != null)
            {
                user.Products.Add(product);
            }

            this.unitOfWork.Commit();
        }

        public void DeleteProductForCurrentUser(int ID)
        {
            User user = this.GetUserEntity();
            Product product = (this.unitOfWork.Products.FindWhere(p => p.Id == ID)).FirstOrDefault();
            if (product != null)
            {
                user.Products.Remove(product);
            }

            this.unitOfWork.Commit();
        }

        public List<Product> GetAllProducts()
        {
            List<Product> result = new List<Product>();
            User user = this.GetUserEntity();
            var usersProductIds = from p in user.Products select p.Id;
            foreach (var product in this.unitOfWork.Products.FindAll().Include("Substance").Include("Container"))
            {
                product.UsedByUser = usersProductIds.Contains(product.Id);
                result.Add(product);
            }

            return result;
        }

        /// <summary>
        /// Gets the user entity.
        /// </summary>
        /// <param name="entities">The entities.</param>
        /// <returns></returns>
        private User GetUserEntity()
        {
            string userName = Thread.CurrentPrincipal.Identity.Name;
            User result = null;

            var userEntities = this.unitOfWork.Users.FindWhere(u => u.Username == userName).Include("Products");
            if (userEntities.Count() == 0)
            {
                User newUser = new User() { Username = userName };
                this.unitOfWork.Users.Add(newUser);
                this.unitOfWork.Commit();
                newUser = this.unitOfWork.Users.FindWhere(u => u.Username == userName).Include("Products").First();
                result = newUser;
                //                throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "Couldn't find a user with user name {0} and ID {1}", userName, guid));
            }
            else
            {
                result = userEntities.First();
            }

            return result;
        }

        public void ProductSearch()
        {
            this.RedirectTo("ProductSearch.aspx");
        }

        protected virtual void RedirectTo(string address)
        {
            Guard.ArgumentNotNullOrEmptyString(address, "address");
            IHttpContext context = new Microsoft.Practices.CompositeWeb.Web.HttpContext(HttpContext.Current);
            context.Server.Transfer(address);
        }

        #region IDisposable Members

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    if (this.unitOfWork != null)
                    {
                        this.unitOfWork.Dispose();
                    }
                }
            }

            this.disposed = true;
        }
        #endregion

        ~ProductsController()
        {
            this.Dispose(false);
        }
    }
}
